from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
from opinion_extractor.respondent.exceptions import NotARespondentException
from opinion_extractor.temporal_model.models import TemporalModel
import exceptions
from utils import get_latest_respondent_profile_for_questionnaire


class OpinionBase(TemporalModel):
    min_days_between_versions = settings.OPINION_EXTRACTOR[
        'min_days_between_opinion_changes'
    ]
    unchangeable_fields = ['user', 'questionnaire']
    
    user = models.ForeignKey(
        User,
        verbose_name=_('user'),
        related_name='opinions',
        editable=False
    )
    questionnaire = models.ForeignKey(
        'questionnaire.Questionnaire',
        verbose_name=_('questionnaire'),
        related_name='opinions'
    )
    num_failled_attempts = models.PositiveIntegerField(
        _('number of failled attempts to change the opinion'),
        db_index=True
    )
    responded_at_time = models.TimeField(
        _('responded at time'),
    )
    response_time_sec = models.PositiveIntegerField(
        _('total response time (seconds)'),
    )
    
    class Meta(TemporalModel.Meta):
        abstract = True
    
    def __unicode__(self):
        return u"Opinion (%s - %s) by '%s' for the '%s' questionnaire" % (
            self.valid_from,
            self.valid_to,
            self.user,
            self.questionnaire
        )   
    
    def save(self, force_insert=False, force_update=False, force_archive=True):
        if not (force_insert or force_update) and force_archive:
            # Sanity check - responding is allowed only for `Live` questionnaires
            q = self.questionnaire
            u = self.user
            if q.publish_status != 'L':
                raise exceptions.RespondingToNonLiveQuestionnaire(u, q)
            
            # Sanity check - responding is allowed only to those with permission
            if not u.has_perm('questionnaire.answer_questionnaire'):
                raise NotARespondentException(u)
            
            meta = self._meta
            num_questions = q.num_questions
            answers = map(lambda x: getattr(self, x), meta.answer_fields)
            
            # Sanity check - all answer[num_questions:] fields must be None
            # (because they are not used)
            # Have to use answers_not_None because of type coercion
            answers_not_None = map(lambda x: x is not None, answers)
            if any(answers_not_None[num_questions:]):
                raise exceptions.TooManyAnswers(u, q)
            
            # Sanity check - all answer[:num_questions] fields must not be None
            # (all questions must be answered)
            # Have to use answers_not_None because of type coercion
            if not all(answers_not_None[:num_questions]):
                raise exceptions.TooFewAnswers(u, q)
            
            # Sanity check - for each question the response must be a valid answer 
            num_answers_per_question = q.questions.values_list(
                'num_answers',
                flat=True
            )
            for i, num_answers in enumerate(num_answers_per_question):
                if answers[i] not in xrange(num_answers):
                    raise exceptions.InvalidAnswer(u, q, i+1)
            
            # Sanity check - the user must satisfy questionnaire.population_filter
            respondent = get_latest_respondent_profile_for_questionnaire(u, q)
            
            # Sync `respondent_model` fields with this model
            for field_name in meta.respondent_fields:
                setattr(self, field_name, getattr(respondent, field_name))
                
        super(OpinionBase, self).save(force_insert, force_update, force_archive)
